from machine import sleep, SoftI2C, Pin, Timer, RTC, I2C,UART,ADC
from utime import ticks_diff, ticks_us
from max30102 import MAX30102, MAX30105_PULSE_AMP_MEDIUM
from hrcalc import calc_hr_and_spo2
import network
import ntptime
import time
import mfrc522
from umqttsimple import MQTTClient

# ------------ 添加新代码 -----------
import ssd1306

button_name = 1
BEATS = 0  # 存储心率
FINGER_FLAG = False  # 默认表示未检测到手指
SPO2 = 0  # 存储血氧
TEMPERATURE = 0  # 存储温度
HREAT_LIST = []  # 存储心率的最新30次数据
SPO2_LIST = []  # 存储血氧的最新10次数据
TEMP_LIST = []  # 存储温度的最新10次数据
spo2_str=""
temp_str=""
bmp_str=""
card1=False
card2=False
# ------------ 添加新代码 -----------
OLED = None
def lightSensor():
    ps2_y = ADC(Pin(36))
    ps2_y.atten(ADC.ATTN_11DB)  # 这里配置测量量程为3.3V
    # 数字量
    p15 = Pin(15, Pin.IN)
    val_y = ps2_y.read()  # 0-4095
    light = p15.value()
    return light
    
class LED:
    def __init__(self):
        self.led =Pin(14,Pin.OUT)
    def LED_ON(self):
        self.led.value(1)
    def LED_OFF(self):
        self.led.value(0)
    
    
def server_connect(ClientID):
    global server,nexttime
    nexttime =time.time()
    server = MQTTClient(ClientID, "124.222.202.175")  # 建立一个MQTT客户端
    server.set_callback(sub_cb)  # 设置回调函数
    server.connect()  # 建立连接
    server.subscribe(b"MQTT")
    server.subscribe(b"med")
    server.subscribe(b"add")
    
def sub_cb(topic, msg):  # 回调函数，收到服务器消息后会调用这个函数
    global nexttime
    print(topic, msg)
    
    
    if(topic.decode("utf-8")=="add"):
        if(int(msg.decode("utf-8"))!=0):
            nexttime =time.time()+(int(msg.decode("utf-8"))*5)
            print(nexttime)

        
        
        
        
    

def init_rc522():
    global reader
    sck = Pin(18, Pin.OUT)
    mosi = Pin(23, Pin.OUT) # Controller out, peripheral in
    miso = Pin(19, Pin.OUT) # Controller in, peripheral out
    rst =Pin(27, Pin.OUT)
    sda=Pin(21, Pin.OUT)
    
    reader = mfrc522.MFRC522(18, 23, 19, 27, 21)
def check_uid():
    global reader,uart,card1,card2

    (status, tag_type) = reader.request(reader.REQIDL)
    if status == reader.OK:
        (status, raw_uid) = reader.anticoll()
        uid ='0x%02x%02x%02x%02x' % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3])
        led.LED_ON()
        time.sleep(1)
        if(uid =="0x349b0104" and card1==False):
            card1=True
            card2=False
            uart.write("success")
            server_connect(uid)
            time.sleep(1)
            led.LED_OFF()
        elif(uid =="0x65739cac"and card2==False):
            card1=False
            card2=True
            uart.write("success")
            server_connect(uid)
            time.sleep(1)
            led.LED_OFF()
        elif(uid =="0x349b0104" or uid =="0x65739cac"):
            uart.write("fail")
            time.sleep(1)
            led.LED_OFF()
        else:
            uart.write("fail1")
            time.sleep(1)
            led.LED_OFF()
            
            
        
            

def button_setup():
    global button_de
    button_de = Pin(33, Pin.IN, Pin.PULL_UP)

    button_de.irq(trigger=Pin.IRQ_FALLING, handler=button_detect)


def button_detect(chn):
    global button_name
    button_name += 1
    if button_name == 2:
        button_name = 0





class Clock:
    msg = ""

    def __init__(self):
        self.connect_wifi('iPhone', '12345678')
        self.ntp()
        self.rtc = RTC()
        

        # 调用这个方法来联网设置时间
        self.second_light = False

    def connect_wifi(self, wifi, password):
        wlan = network.WLAN(network.STA_IF)
        wlan.active(True)
        if not wlan.isconnected():
            print('connecting to network...')
            wlan.connect(wifi, password)
            i = 1
            while not wlan.isconnected() and i < 10:
                print("正在链接...{}".format(i))
                i += 1
                time.sleep(1)
        print('network config:', wlan.ifconfig())

    def ntp(self):
        self.connect_wifi('iPhone', '12345678')
        # 东八区 UTC+8偏移时间（秒）
        try:
            ntptime.host = "ntp1.aliyun.com"
            ntptime.settime()
        except:
            time.sleep(1)
            ntptime.settime()
            pass
            
        
        

    def get_message(self):
        global msg
        return msg

    def get_time(self):
        global msg
        date = self.rtc.datetime()
        if (date[0] == 2000):
            msg = "Networking error"
        else:
            msg = "Online"

        # 获取真正的时间

        return date


def create_oled0():
    global OLED
    # set a specific date and time
    # 创建I2C对象(驱动屏幕)
    i2c_oled = SoftI2C(scl=Pin(25), sda=Pin(26))

    # 宽度高度
    oled_width = 128
    oled_height = 64
    # 创建oled屏幕对象
    OLED = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c_oled)

    OLED.fill(0)

    OLED.text('Wifi connecting', 0, 30)  # 初始化屏幕显示的内容
    OLED.show()
    
        





def display_info(t):  # 代码中循环使用的内容 1秒循环一次
    # ------------ 修改代码 -----------
    global HREAT_LIST, SPO2_LIST, TEMP_LIST, OLED, BEATS, SPO2, TEMPERATURE,button_name
    lightSensor()


    if button_name==1:
        OLED.fill(0)
        show_time()
        OLED.text(clock.get_message(), 0, 50)
        # OLED屏幕显示 放手指
        OLED.show()
    elif button_name ==0:
        show_time()
        if FINGER_FLAG is False :
            OLED.fill(0)
            OLED.text('Put your finger', 0, 10)
            OLED.text('for testing', 0, 20)
            OLED.show()
            HREAT_LIST.clear()
            SPO2_LIST.clear()
            TEMP_LIST.clear()
        # ------------ 添加新代码 -----------
            BEATS = SPO2 = TEMPERATURE = 0
            return
    # ------------ 添加新代码 -----------
        if len(HREAT_LIST) < 30:
            bmp_str = 'BPM :%3d' % int(BEATS)
            server.publish(b"MQTT", str(BEATS))
        else:
            bmp_str = 'BPM :%3d' % int(sum(HREAT_LIST[20:]) / len(HREAT_LIST[20:]))

        if len(SPO2_LIST) < 10:
            spo2_str = 'SPO2:%3d' % int(SPO2)
            server.publish(b"spo2", str(SPO2))
        else:
            spo2_str = 'SPO2:%3d' % int(sum(SPO2_LIST[5:]) / len(SPO2_LIST[5:]))

        if len(TEMP_LIST) < 10:
            temp_str = 'TEMP:%3d' % int(TEMPERATURE)
            server.publish(b"temp", str(TEMPERATURE))
        else:
            temp_str = 'TEMP:%3d' % int(sum(TEMP_LIST[5:]) / len(TEMP_LIST[5:]))

        if len(HREAT_LIST) < 30:
            HREAT_LIST.append(BEATS)
            HREAT_LIST = HREAT_LIST[-30:]  # 保证列表最多30个元素
            bmp_str += " <-- %d" % (30 - len(HREAT_LIST))
        elif len(SPO2_LIST) < 10:
            SPO2_LIST.append(SPO2)
            SPO2_LIST = SPO2_LIST[-10:]  # 保证列表最多10个元素
            spo2_str += " <-- %d" % (10 - len(SPO2_LIST))
        elif len(TEMP_LIST) < 10:
            TEMP_LIST.append(TEMPERATURE)
            TEMP_LIST = TEMP_LIST[-10:]  # 保证列表最多10个元素
            temp_str += " <-- %d" % (10 - len(TEMP_LIST))
            #不显示手指
        
        OLED.fill(0)
        OLED.text(bmp_str, 0, 30)
        OLED.text(spo2_str, 0, 40)
        OLED.text(temp_str, 0, 50)
        OLED.show()
        

def show_time():
    global timeNow,uart,nexttime
    date = clock.get_time()
    date_hour = date[4]+8 
    day = date[2]
    #if(callday==day and )
    if date_hour>24:
        date_hour=date_hour-24
        day=day+1
    if(time.time()==nexttime):
        uart.write("medicine")
        print(1)

        
   


    

    # 清空
    year = str(date[0])
    month = str(date[1])
    day = str(date[2])
    hour = str(date_hour)
    minutes = str(date[5])
    s = str(date[6])
    
    
    if date[3] == 0:
        dayplus = "Monday"
    elif date[3] == 1:
        dayplus = "Tuesday"
    elif date[3] == 2:
        dayplus = "Wednesday"
    elif date[3] == 3:
        dayplus = "Thursday"
    elif date[3] == 4:
        dayplus = "Friday"
    elif date[3] == 5:
        dayplus = "Saturday"
    elif date[3] == 6:
        dayplus = "Sunday"
    if date_hour < 10:
        hour = "0" + str(date_hour)
    if date[5] < 10:
        minutes = "0" + str(date[5])
    if date[6] < 10:
        s = "0" + str(date[6])
    if(date[2]<10):
        day = "0"+ str(date[2])
    if(date[1]<10):
        month = "0"+ str(date[1])
    timeNow =month+day+hour+minutes
    

    # print(hour)
    OLED.text(year + "/" + month + "/" + day, 0, 0)
    OLED.text(dayplus, 0, 10)
    OLED.text(hour + ":" + minutes + ":" + s, 0, 20)

    # 时（2位）

    # 显示


def dataGetting():
    global BEATS, FINGER_FLAG, SPO2, TEMPERATURE,timeNow,server  # 如果需要对全局变量修改，则需要global声明

    # 创建I2C对象(检测MAX30102)
    i2c = SoftI2C(sda=Pin(16), scl=Pin(17), freq=400000)  # Fast: 400kHz, slow: 100kHz

    # 创建传感器对象
    sensor = MAX30102(i2c=i2c)

    # 检测是否有传感器
    if sensor.i2c_address not in i2c.scan():
        print("没有找到传感器")
        return
    elif not (sensor.check_part_id()):
        # 检查传感器是否兼容
        print("检测到的I2C设备不是MAX30102或者MAX30105")
        return
    else:
        print("传感器已识别到")

    # 配置
    sensor.setup_sensor()
    sensor.set_sample_rate(400)
    sensor.set_fifo_average(8)
    sensor.set_active_leds_amplitude(MAX30105_PULSE_AMP_MEDIUM)

    t_start = ticks_us()  # Starting time of the acquisition

    MAX_HISTORY = 32
    history = []
    beats_history = []
    beat = False
    red_list = []
    ir_list = []
    time=0

    while True:
        
        server.check_msg()
        if(uart.any()):
            msgUart =uart.readline()
            if msgUart==b'gettime':
                print(timeNow)
                uart.write(timeNow)
            elif msgUart==b'lighton':
                led.LED_ON()
                print("lighton")
                
                    
            elif msgUart==b'location':
                if(lightSensor()==1):
                    print(lightSensor())
                    led.LED_ON()
                    print("location")
                else:
                    led.LED_OFF()
                
            elif msgUart==b'lightoff':
                led.LED_OFF()
                print("lightoff")
        


        sensor.check()
        if sensor.available():
            # FIFO 先进先出，从队列中取数据。都是整形int
            red_reading = sensor.pop_red_from_storage()
            ir_reading = sensor.pop_ir_from_storage()

            if red_reading < 1000 :
                history.clear()
                beats_history.clear()
                FINGER_FLAG = False  # 表示没有放手指
                check_uid()
                
                continue
            else:
                FINGER_FLAG = True  # 表示手指已放

            # ------------ 修改代码 -----------
            if len(HREAT_LIST) < 30:
                # 计算心率
                history.append(red_reading)

                # 为了防止列表过大，这里取列表的后32个元素
                history = history[-MAX_HISTORY:]

                # 提取必要数据
                minima, maxima = min(history), max(history)
                threshold_on = (minima + maxima * 3) // 4  # 3/4
                threshold_off = (minima + maxima) // 2  # 1/2

                if not beat and red_reading > threshold_on:
                    beat = True
                    t_us = ticks_diff(ticks_us(), t_start)
                    t_s = t_us / 1000000
                    f = 1 / t_s
                    bpm = f * 60
                    if bpm < 500:
                        t_start = ticks_us()
                        beats_history.append(bpm)
                        beats_history = beats_history[-MAX_HISTORY:]  # 只保留最大30个元素数据
                        BEATS = round(sum(beats_history) / len(beats_history), 2)  # 四舍五入
                if beat and red_reading < threshold_off:
                    beat = False
            # ------------ 修改代码 -----------
            elif len(SPO2_LIST) < 10:
                # 计算血氧
                red_list.append(red_reading)
                ir_list.append(ir_reading)
                # 最多 只保留最新的100个
                red_list = red_list[-100:]
                ir_list = ir_list[-100:]
                # 计算血氧值
                if len(red_list) == 100 and len(ir_list) == 100:
                    hr, hrb, sp, spb = calc_hr_and_spo2(red_list, ir_list)
                    if hrb is True and spb is True:
                        if sp != -999:
                            SPO2 = int(sp)
            # ------------ 修改代码 -----------
            elif len(TEMP_LIST) < 10:
                # 计算温度
                TEMPERATURE = sensor.read_temperature()
            


if __name__ == '__main__':
    uart = UART(2, baudrate=9600, rx=2,tx=4,timeout=10)
    init_rc522()
    create_oled0()
    led=LED()
    clock = Clock()
    server_connect("sbJY")
    


    

    # 监控ledctl这个通道，接收控制命令
    
    # 1. 创建定时器
    timer = Timer(1)
    
    # 2. 设置定时器的回调函数，每1秒钟调用1次display_info函数（用来显示数据）
    timer.init(period=1000, mode=Timer.PERIODIC, callback=display_info)
    button_setup()
    

    # ------------ 添加新代码 -----------
    # 3. 创建OLED屏幕

    # 4. 用来检测数据
    dataGetting()

